diff --git a/swh/web/assets/src/bundles/browse/origin-search.js b/swh/web/assets/src/bundles/browse/origin-search.js
index c5e68325..50f99bb7 100644
--- a/swh/web/assets/src/bundles/browse/origin-search.js
+++ b/swh/web/assets/src/bundles/browse/origin-search.js
@@ -1,238 +1,232 @@
/**
* Copyright (C) 2018-2019 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
import {heapsPermute} from 'utils/heaps-permute';
import {handleFetchError} from 'utils/functions';
let originPatterns;
let perPage = 100;
let limit = perPage * 2;
let offset = 0;
let currentData = null;
let inSearch = false;
function fixTableRowsStyle() {
setTimeout(() => {
$('#origin-search-results tbody tr').removeAttr('style');
});
}
function clearOriginSearchResultsTable() {
$('#origin-search-results tbody tr').remove();
}
function populateOriginSearchResultsTable(origins, offset) {
let localOffset = offset % limit;
if (origins.length > 0) {
$('#swh-origin-search-results').show();
$('#swh-no-result').hide();
clearOriginSearchResultsTable();
let table = $('#origin-search-results tbody');
for (let i = localOffset; i < localOffset + perPage && i < origins.length; ++i) {
let origin = origins[i];
let browseUrl = Urls.browse_origin(origin.url);
let tableRow = `
';
table.append(tableRow);
// get async latest visit snapshot and update visit status icon
let latestSnapshotUrl = Urls.api_1_origin_visit_latest(origin.url);
latestSnapshotUrl += '?require_snapshot=true';
fetch(latestSnapshotUrl)
.then(response => response.json())
.then(data => {
$(`#visit-status-origin-${i}`).children().remove();
if (data) {
$(`#visit-status-origin-${i}`).append('');
} else {
$(`#visit-status-origin-${i}`).append('');
if ($('#swh-filter-empty-visits').prop('checked')) {
$(`#origin-${i}`).remove();
}
}
});
}
fixTableRowsStyle();
} else {
$('#swh-origin-search-results').hide();
$('#swh-no-result').text('No origins matching the search criteria were found.');
$('#swh-no-result').show();
}
if (origins.length - localOffset < perPage ||
(origins.length < limit && (localOffset + perPage) === origins.length)) {
$('#origins-next-results-button').addClass('disabled');
} else {
$('#origins-next-results-button').removeClass('disabled');
}
if (offset > 0) {
$('#origins-prev-results-button').removeClass('disabled');
} else {
$('#origins-prev-results-button').addClass('disabled');
}
inSearch = false;
setTimeout(() => {
window.scrollTo(0, 0);
});
}
function escapeStringRegexp(str) {
let matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
return str.replace(matchOperatorsRe, '\\\\\\$&');
}
function searchOrigins(patterns, limit, searchOffset, offset) {
let baseSearchUrl;
let searchMetadata = $('#swh-search-origin-metadata').prop('checked');
if (searchMetadata) {
baseSearchUrl = Urls.api_1_origin_metadata_search() + `?fulltext=${patterns}`;
} else {
originPatterns = patterns;
let patternsArray = patterns.trim().replace(/\s+/g, ' ').split(' ');
for (let i = 0; i < patternsArray.length; ++i) {
patternsArray[i] = escapeStringRegexp(patternsArray[i]);
}
// url length must be less than 4096 for modern browsers
// assuming average word length, 6 is max patternArray.length
if (patternsArray.length < 7) {
let patternsPermut = [];
heapsPermute(patternsArray, p => patternsPermut.push(p.join('.*')));
let regex = patternsPermut.join('|');
baseSearchUrl = Urls.browse_origin_search(regex) + `?regexp=true`;
} else {
baseSearchUrl = Urls.browse_origin_search(patternsArray.join('.*')) + `?regexp=true`;
}
}
let withVisit = $('#swh-search-origins-with-visit').prop('checked');
let searchUrl = baseSearchUrl + `&limit=${limit}&offset=${searchOffset}&with_visit=${withVisit}`;
clearOriginSearchResultsTable();
$('.swh-loading').addClass('show');
fetch(searchUrl)
.then(handleFetchError)
.then(response => response.json())
.then(data => {
currentData = data;
$('.swh-loading').removeClass('show');
populateOriginSearchResultsTable(data, offset);
})
.catch(response => {
$('.swh-loading').removeClass('show');
inSearch = false;
$('#swh-origin-search-results').hide();
$('#swh-no-result').text(`Error ${response.status}: ${response.statusText}`);
$('#swh-no-result').show();
});
}
function doSearch() {
$('#swh-no-result').hide();
let patterns = $('#origins-url-patterns').val();
offset = 0;
inSearch = true;
// first try to resolve a swh persistent identifier
let resolvePidUrl = Urls.api_1_resolve_swh_pid(patterns);
fetch(resolvePidUrl)
.then(handleFetchError)
.then(response => response.json())
.then(data => {
// pid has been successfully resolved,
// so redirect to browse page
window.location = data.browse_url;
})
.catch(response => {
// pid resolving failed
if (patterns.startsWith('swh:')) {
// display a useful error message if the input
// looks like a swh pid
response.json().then(data => {
$('#swh-origin-search-results').hide();
$('.swh-search-pagination').hide();
$('#swh-no-result').text(data.reason);
$('#swh-no-result').show();
});
} else {
// otherwise, proceed with origins search
$('#swh-origin-search-results').show();
$('.swh-search-pagination').show();
searchOrigins(patterns, limit, offset, offset);
}
});
}
export function initOriginSearch() {
$(document).ready(() => {
$('#swh-search-origins').submit(event => {
event.preventDefault();
let patterns = $('#origins-url-patterns').val().trim();
let withVisit = $('#swh-search-origins-with-visit').prop('checked');
let withContent = $('#swh-filter-empty-visits').prop('checked');
let searchMetadata = $('#swh-search-origin-metadata').prop('checked');
let queryParameters = '?q=' + encodeURIComponent(patterns);
if (withVisit) {
queryParameters += '&with_visit';
}
if (withContent) {
queryParameters += '&with_content';
}
if (searchMetadata) {
queryParameters += '&search_metadata';
}
// Update the url, triggering page reload and effective search
window.location.search = queryParameters;
});
$('#origins-next-results-button').click(event => {
if ($('#origins-next-results-button').hasClass('disabled') || inSearch) {
return;
}
inSearch = true;
offset += perPage;
if (!currentData || (offset >= limit && offset % limit === 0)) {
searchOrigins(originPatterns, limit, offset, offset);
} else {
populateOriginSearchResultsTable(currentData, offset);
}
event.preventDefault();
});
$('#origins-prev-results-button').click(event => {
if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) {
return;
}
inSearch = true;
offset -= perPage;
if (!currentData || (offset > 0 && (offset + perPage) % limit === 0)) {
searchOrigins(originPatterns, limit, (offset + perPage) - limit, offset);
} else {
populateOriginSearchResultsTable(currentData, offset);
}
event.preventDefault();
});
- $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', e => {
- if (e.currentTarget.text.trim() === 'Search') {
- fixTableRowsStyle();
- }
- });
-
let urlParams = new URLSearchParams(window.location.search);
let query = urlParams.get('q');
let withVisit = urlParams.has('with_visit');
let withContent = urlParams.has('with_content');
let searchMetadata = urlParams.has('search_metadata');
if (query) {
$('#origins-url-patterns').val(query);
$('#swh-search-origins-with-visit').prop('checked', withVisit);
$('#swh-filter-empty-visits').prop('checked', withContent);
$('#swh-search-origin-metadata').prop('checked', searchMetadata);
doSearch();
}
});
}
diff --git a/swh/web/assets/src/bundles/vault/vault-ui.js b/swh/web/assets/src/bundles/vault/vault-ui.js
index d08d2302..f056ddd5 100644
--- a/swh/web/assets/src/bundles/vault/vault-ui.js
+++ b/swh/web/assets/src/bundles/vault/vault-ui.js
@@ -1,252 +1,245 @@
/**
* Copyright (C) 2018-2019 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
import {handleFetchError, handleFetchErrors, csrfPost} from 'utils/functions';
let progress = `
;`;
let pollingInterval = 5000;
let checkVaultId;
function updateProgressBar(progressBar, cookingTask) {
if (cookingTask.status === 'new') {
progressBar.css('background-color', 'rgba(128, 128, 128, 0.5)');
} else if (cookingTask.status === 'pending') {
progressBar.css('background-color', 'rgba(0, 0, 255, 0.5)');
} else if (cookingTask.status === 'done') {
progressBar.css('background-color', '#5cb85c');
} else if (cookingTask.status === 'failed') {
progressBar.css('background-color', 'rgba(255, 0, 0, 0.5)');
progressBar.css('background-image', 'none');
}
progressBar.text(cookingTask.progress_message || cookingTask.status);
if (cookingTask.status === 'new' || cookingTask.status === 'pending') {
progressBar.addClass('progress-bar-animated');
} else {
progressBar.removeClass('progress-bar-striped');
}
}
let recookTask;
// called when the user wants to download a cooked archive
export function fetchCookedObject(fetchUrl) {
recookTask = null;
// first, check if the link is still available from the vault
fetch(fetchUrl)
.then(response => {
// link is still alive, proceed to download
if (response.ok) {
$('#vault-fetch-iframe').attr('src', fetchUrl);
// link is dead
} else {
// get the associated cooking task
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
for (let i = 0; i < vaultCookingTasks.length; ++i) {
if (vaultCookingTasks[i].fetch_url === fetchUrl) {
recookTask = vaultCookingTasks[i];
break;
}
}
// display a modal asking the user if he wants to recook the archive
$('#vault-recook-object-modal').modal('show');
}
});
}
// called when the user wants to recook an archive
// for which the download link is not available anymore
export function recookObject() {
if (recookTask) {
// stop cooking tasks status polling
clearTimeout(checkVaultId);
// build cook request url
let cookingUrl;
if (recookTask.object_type === 'directory') {
cookingUrl = Urls.api_1_vault_cook_directory(recookTask.object_id);
} else {
cookingUrl = Urls.api_1_vault_cook_revision_gitfast(recookTask.object_id);
}
if (recookTask.email) {
cookingUrl += '?email=' + recookTask.email;
}
// request archive cooking
csrfPost(cookingUrl)
.then(handleFetchError)
.then(() => {
// update task status
recookTask.status = 'new';
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
for (let i = 0; i < vaultCookingTasks.length; ++i) {
if (vaultCookingTasks[i].object_id === recookTask.object_id) {
vaultCookingTasks[i] = recookTask;
break;
}
}
// save updated tasks to local storage
localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks));
// restart cooking tasks status polling
checkVaultCookingTasks();
// hide recook archive modal
$('#vault-recook-object-modal').modal('hide');
})
// something went wrong
.catch(() => {
checkVaultCookingTasks();
$('#vault-recook-object-modal').modal('hide');
});
}
}
function checkVaultCookingTasks() {
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
if (!vaultCookingTasks || vaultCookingTasks.length === 0) {
$('.swh-vault-table tbody tr').remove();
checkVaultId = setTimeout(checkVaultCookingTasks, pollingInterval);
return;
}
let cookingTaskRequests = [];
let tasks = {};
let currentObjectIds = [];
for (let i = 0; i < vaultCookingTasks.length; ++i) {
let cookingTask = vaultCookingTasks[i];
currentObjectIds.push(cookingTask.object_id);
tasks[cookingTask.object_id] = cookingTask;
let cookingUrl;
if (cookingTask.object_type === 'directory') {
cookingUrl = Urls.api_1_vault_cook_directory(cookingTask.object_id);
} else {
cookingUrl = Urls.api_1_vault_cook_revision_gitfast(cookingTask.object_id);
}
if (cookingTask.status !== 'done' && cookingTask.status !== 'failed') {
cookingTaskRequests.push(fetch(cookingUrl));
}
}
$('.swh-vault-table tbody tr').each((i, row) => {
let objectId = $(row).find('.vault-object-id').data('object-id');
if ($.inArray(objectId, currentObjectIds) === -1) {
$(row).remove();
}
});
Promise.all(cookingTaskRequests)
.then(handleFetchErrors)
.then(responses => Promise.all(responses.map(r => r.json())))
.then(cookingTasks => {
let table = $('#vault-cooking-tasks tbody');
for (let i = 0; i < cookingTasks.length; ++i) {
let cookingTask = tasks[cookingTasks[i].obj_id];
cookingTask.status = cookingTasks[i].status;
cookingTask.fetch_url = cookingTasks[i].fetch_url;
cookingTask.progress_message = cookingTasks[i].progress_message;
}
for (let i = 0; i < vaultCookingTasks.length; ++i) {
let cookingTask = vaultCookingTasks[i];
let rowTask = $('#vault-task-' + cookingTask.object_id);
let downloadLinkWait = 'Waiting for download link to be available';
if (!rowTask.length) {
let browseUrl;
if (cookingTask.object_type === 'directory') {
browseUrl = Urls.browse_directory(cookingTask.object_id);
} else {
browseUrl = Urls.browse_revision(cookingTask.object_id);
}
let progressBar = $.parseHTML(progress)[0];
let progressBarContent = $(progressBar).find('.progress-bar');
updateProgressBar(progressBarContent, cookingTask);
let tableRow;
if (cookingTask.object_type === 'directory') {
tableRow = `